package com.aruba.attendance.ui.widget.calendar

import android.annotation.SuppressLint
import androidx.compose.animation.*
import androidx.compose.animation.core.*
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.lazy.grid.GridCells
import androidx.compose.foundation.lazy.grid.LazyVerticalGrid
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.*
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.scale
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp
import com.google.accompanist.pager.ExperimentalPagerApi
import com.google.accompanist.pager.HorizontalPager
import com.google.accompanist.pager.rememberPagerState
import kotlin.math.abs

/**
 * 日历优化
 *
 * 原作者：https://toscode.gitee.com/fanketly/Compose_accompanist/tree/master
 *
 * Created by aruba
 */

private val weekList = listOf("一", "二", "三", "四", "五", "六", "日")

/**
 * @param modifier 样式设置
 * @param onDateSelected 点击监听器
 */
@OptIn(ExperimentalPagerApi::class)
@SuppressLint("ModifierParameter")
@Composable
@JvmOverloads
fun CalendarView(
    modifier: Modifier = Modifier.wrapContentHeight(),
    fixedHeight: Boolean = false,// 固定高度为7*6
    startYear: Int = 2020,// 日历开始的年份  默认从2020开始
    endYear: Int = -1,// 日历结束的年份  默认今年
    initialSelectDate: SelectDateState? = null,//选中的日期与跳转状态 yyyyMMdd
    onDateSelected: (year: Int, month: Int, dayOfMonth: Int) -> Unit = { _, _, _ -> },
    pagerContent: @Composable (ColumnScope.() -> Unit)? = null,
    content: @Composable BoxScope.() -> Unit
) {
    val date = java.util.Calendar.getInstance()

    val nowYear = date.get(java.util.Calendar.YEAR)
    val nowMonth = date.get(java.util.Calendar.MONTH) + 1
    val nowDayOfMonth = date.get(java.util.Calendar.DAY_OF_MONTH)
    val today = getDateStr(nowYear, nowMonth, nowDayOfMonth)

    require(startYear <= nowYear)
    if (initialSelectDate != null)
        require(initialSelectDate.selectDate.value <= today)
    if (endYear != -1) require(endYear >= nowYear)

    val totalPagerCount =
        ((if (endYear == -1) nowYear else endYear) - startYear) * 12 +
                date.get(java.util.Calendar.MONTH) + 1
    val selectDate = initialSelectDate?.selectDate ?: rememberSaveable { mutableStateOf(today) }
    val pagerState = rememberPagerState(
        (nowYear - startYear) * 12 +
                date.get(java.util.Calendar.MONTH)
    )

    if (initialSelectDate != null) {
        // 跳转页码
        LaunchedEffect(initialSelectDate.startRedirectToCurrentPage.value) {
            if (!initialSelectDate.startRedirectToCurrentPage.value) return@LaunchedEffect
            initialSelectDate.startRedirectToCurrentPage.value = false
            
            val initY = initialSelectDate.selectDate.value.substring(0, 4).toInt()
            val initM = initialSelectDate.selectDate.value.substring(4, 6).toInt()

            require(initY >= startYear)
            pagerState.scrollToPage((initY - startYear) * 12 + initM - 1)
        }
    }


    Column(modifier = modifier) {
        HorizontalPager(
            count = totalPagerCount,
            state = pagerState,
            verticalAlignment = Alignment.Top
        ) { index ->
            if (abs(pagerState.currentPage - index) < 2) {// 缓解pager滑动卡顿
                val itemMonth = index % 12 + 1
                val itemYear = startYear + index / 12
                Month(
                    selectDate, itemMonth, itemYear, today,
                    MonthEnum.isLeapYear(itemYear), itemYear == nowYear && itemMonth == nowMonth,
                    fixedHeight, onDateSelected
                ) {
                    pagerContent?.invoke(this@Column)
                }
            }
        }

        Box(modifier = Modifier.fillMaxSize()) {
            content()
        }
    }

}


/**
 *@param selectDate 动态更新
 *@param onDateSelected 点击高亮
 */
@Composable
fun Month(
    selectDate: MutableState<String>,
    month: Int,
    year: Int,
    today: String,
    leapYear: Boolean,
    isNowMonth: Boolean,
    fixedHeight: Boolean = false,// 固定高度为7*6
    onDateSelected: (year: Int, month: Int, dayOfMonth: Int) -> Unit,
    content: @Composable ColumnScope.() -> Unit = {}
) {
    Column(modifier = Modifier.wrapContentHeight()) {
        Text(
            text = "${year}年${month}月",
            modifier = Modifier
                .fillMaxWidth()
                .padding(top = 16.dp, bottom = 16.dp),
            textAlign = TextAlign.Center,
            fontWeight = FontWeight.Bold,
            style = MaterialTheme.typography.titleLarge
        )
        Row {
            weekList.forEach {
                Text(
                    text = it,
                    modifier = Modifier.weight(1f),
                    textAlign = TextAlign.Center,
                    style = MaterialTheme.typography.bodyLarge
                )
            }
        }
        LazyVerticalGrid(columns = GridCells.Fixed(7), userScrollEnabled = false) {
            val preEmptyDay = getWeek(year, month)
            items(preEmptyDay) {
                Text(text = "")
            }
            val effectDayLength = MonthEnum.of(month).length(leapYear)
            items(effectDayLength) { index ->
                var textNormalColor = MaterialTheme.colorScheme.onBackground
                if ((index + preEmptyDay) % 7 == 5 || (index + preEmptyDay) % 7 == 6) {// 周末文字字体显示红色
                    textNormalColor = MaterialTheme.colorScheme.error
                }
                Day(
                    day = index + 1,
                    month = month,
                    year = year,
                    today = today,
                    selectDate = selectDate,
                    textNormalColor = textNormalColor,
                    disableDay = isNowMonth && index >= today.substring(6, 8).toInt(),
                    onDateSelected = onDateSelected
                )
            }
            if (fixedHeight) {
                items(42 - effectDayLength - preEmptyDay) {
                    Text(
                        text = " ",
                        modifier = Modifier.padding(4.dp),
                        style = MaterialTheme.typography.bodyLarge
                    )
                }
            }
        }

        content()
    }
}


@Composable
fun Day(
    day: Int,
    month: Int,
    year: Int,
    today: String,
    selectDate: MutableState<String>,
    textNormalColor: Color = MaterialTheme.colorScheme.onBackground,
    disableDay: Boolean,
    onDateSelected: (year: Int, month: Int, dayOfMonth: Int) -> Unit
) {
    val date = getDateStr(year, month, day)

    var animeStarted by remember { mutableStateOf(false) }
    if (date != selectDate.value) animeStarted = false
    val scale by animateFloatAsState(
        targetValue = if (date == selectDate.value) 1f else 0.5f,
        spring(0.35f, stiffness = Spring.StiffnessMedium)
    ) {
        animeStarted = false
    }

    Text(
        text = day.toString(),
        style = MaterialTheme.typography.bodyLarge,
        textAlign = TextAlign.Center,
        fontWeight = if (date == today) FontWeight.Bold else MaterialTheme.typography.bodyLarge.fontWeight,
        color = if (date == selectDate.value)
            MaterialTheme.colorScheme.onPrimary
        else if (date == today)
            MaterialTheme.colorScheme.primary
        else if (disableDay)
            MaterialTheme.colorScheme.onSurface.copy(alpha = 0.12f)
        else textNormalColor,
        modifier = Modifier
            .padding(4.dp)
            .scale(if (animeStarted) scale else 1f)
            .background(
                color = if (date == selectDate.value) MaterialTheme.colorScheme.primary else MaterialTheme.colorScheme.background,
                shape = CircleShape
            )
            .clickable(
                enabled = !disableDay,
                interactionSource = remember { MutableInteractionSource() },
                indication = null,
                onClick = {
                    animeStarted = true
                    selectDate.value = date
                    onDateSelected(year, month, day)
                })
    )
}

private fun getDateStr(year: Int, month: Int, dayOfMonth: Int): String {
    return "$year" +
            "${if (month < 10) "0$month" else month}" +
            "${if (dayOfMonth < 10) "0$dayOfMonth" else dayOfMonth}"
}

/**
 * 泰勒公式:获取当天星期数,0为星期一
 */
private fun getWeek(year: Int, month: Int): Int {
    var m = month
    var y = year % 100
    if (month == 1) {
        m = 13
        y -= 1
    } else if (month == 2) {
        m = 14
        y -= 1
    }

    return (1 + 2 * m + 3 * (m + 1) / 5 + y + y / 4 - y / 100 + y / 400) % 7
}